package sf.database.jdbc.type;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

/**
 * 接受枚举,字符串,数字设值
 * @param <E>
 */
public class EnumOrdinalTypeHandler<E extends Enum<E>> implements TypeHandler<E> {
    protected Class<E> type;
    protected final E[] enums;

    public EnumOrdinalTypeHandler(Class<E> type) {
        if (type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.type = type;
        this.enums = type.getEnumConstants();
        if (this.enums == null) {
            throw new IllegalArgumentException(type.getSimpleName() + " does not represent an enum type.");
        }
    }

    @Override
    public E get(ResultSet rs, String columnName) throws SQLException {
        int i = rs.getInt(columnName);
        if (rs.wasNull()) {
            return null;
        } else {
            return getEnum(type, enums, i);
        }
    }

    @Override
    public E get(ResultSet rs, int index) throws SQLException {
        int i = rs.getInt(index);
        if (rs.wasNull()) {
            return null;
        } else {
            return getEnum(type, enums, i);
        }
    }

    @Override
    public E get(CallableStatement cs, int index) throws SQLException {
        int i = cs.getInt(index);
        if (cs.wasNull()) {
            return null;
        } else {
            return getEnum(type, enums, i);
        }
    }

    @Override
    public E get(CallableStatement cs, String parameterName) throws SQLException {
        int i = cs.getInt(parameterName);
        if (cs.wasNull()) {
            return null;
        } else {
            return getEnum(type, enums, i);
        }
    }

    @Override
    public void update(ResultSet rs, String columnLabel, Object value) throws SQLException {
        Integer ordinal = getOrdinal(value);
        rs.updateInt(columnLabel, ordinal);
    }

    @Override
    public void update(ResultSet rs, int columnIndex, Object value) throws SQLException {
        Integer ordinal = getOrdinal(value);
        rs.updateInt(columnIndex, ordinal);
    }

    @Override
    public Object set(PreparedStatement ps, Object obj, int index) throws SQLException {
        if (null == obj) {
            ps.setNull(index, getSqlType());
        } else {
            Integer ordinal = getOrdinal(obj);
            ps.setInt(index, ordinal);
        }
        return null;
    }

    @Override
    public Object set(CallableStatement cs, Object obj, String parameterName) throws SQLException {
        if (null == obj) {
            cs.setNull(parameterName, getSqlType());
        } else {
            Integer ordinal = getOrdinal(obj);
            cs.setInt(parameterName, ordinal);
        }
        return null;
    }

    @Override
    public int getSqlType() {
        return Types.INTEGER;
    }

    @Override
    public Class<E> getDefaultJavaType() {
        return type;
    }

    public Integer getOrdinal(Object obj) {
        Integer value = null;
        if (obj instanceof Number) {
            E e = getEnum(type, enums, ((Number) obj).intValue());
            value = e.ordinal();
        } else if (obj instanceof String) {
            E e = Enum.valueOf(type, (String) obj);
            value = e.ordinal();
        } else {
            value = ((E) obj).ordinal();
        }
        return value;
    }

    public static <T extends Enum<T>> T getEnum(Class<T> clz, T[] enums, int i) {
        try {
            return enums[i];
        } catch (Exception ex) {
            throw new IllegalArgumentException(
                    "Cannot convert " + i + " to " + clz.getSimpleName() + " by ordinal value.", ex);
        }
    }
}
